package com.sheng.project.component;

import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ApplicationContextAware：获取 spring 容器的 bean
 * Spring 容器会检测容器中的所有 Bean，如果发现某个 Bean 实现了 ApplicationContextAware 接口，
 * Spring 容器会在创建该 Bean 之后，自动调用该 Bean 的 setApplicationContextAware() 方法，调用该方法时，
 * 会将容器本身作为参数传给该方法——该方法中的实现部分将 Spring 传入的参数（容器本身）赋给该类对象的 applicationContext 实例变量，
 * 因此接下来可以通过该 applicationContext 实例变量来访问容器本身。
 *
 * @author liusheng
 * @date 2022/08/17
 */
@Slf4j
@NoArgsConstructor
@Component
public class Mappers implements ApplicationContextAware {

    private static final Map<String, Object> CACHE = new ConcurrentHashMap<>();
    private final String NAMESPACE = "Mapper";

    /**
     * +
     * 通过类的类对象获取的 bean 实例
     *
     * @param mapperClass 类的类对象
     * @return 类的实例对象
     */
    public static <Mapper> Mapper get(Class<Mapper> mapperClass) {
        Object mapper = CACHE.get(mapperClass.getName());
        return mapperClass.cast(mapper);
    }

    /**
     * bean 创建时自动调用，将 mapper 层的 bean 容器放入 CACHE 中
     *
     * @param applicationContext spring 容器
     * @throws BeansException 异常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取所有的 Mapper 层 bean，放入到 CACHE 中
        applicationContext.getBeansWithAnnotation(Mapper.class).forEach((key, value) -> {
            Class<?> mapperClassFormProxy = getMapperClassFromProxy(key, value);
//            Class<?> mapperClassFormProxy = getMapperClassFormProxy(value);
            if (mapperClassFormProxy != null) {
                log.info("获取 {} 层的 Bean：{}，Proxy Bean：{}", NAMESPACE, mapperClassFormProxy.getName(), value);
                CACHE.put(mapperClassFormProxy.getName(), value);
            }
        });
    }

    /**
     * 获取类的类对象
     *
     * @param k
     * @param v
     * @return
     */
    private Class<?> getMapperClassFromProxy(String k, Object v) {
        k = k.toUpperCase();
        Type[] mapperClasses = v.getClass().getGenericInterfaces();
        for (Type mapperClass : mapperClasses) {
            String originClassName = ((Class<?>) mapperClass).getSimpleName().toUpperCase();
            if (k.equals(originClassName)) {
                return ((Class<?>) mapperClass);
            }
        }
        return null;
    }

    public Class<?> getMapperClassFormProxy(Object o) {
        Object target = AopProxyUtils.getSingletonTarget(o);
        return target != null ? target.getClass() : null;
    }
}

